home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 10 / AACD 10.iso / AACD / Games / WarpQuake / Src / cd_audio.c < prev    next >
C/C++ Source or Header  |  2000-05-22  |  19KB  |  887 lines

  1. /*
  2. Copyright (C) 1996-1997 Id Software, Inc.
  3.  
  4. This program is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU General Public License
  6. as published by the Free Software Foundation; either version 2
  7. of the License, or (at your option) any later version.
  8.  
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
  12.  
  13. See the GNU General Public License for more details.
  14.  
  15. You should have received a copy of the GNU General Public License
  16. along with this program; if not, write to the Free Software
  17. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  18.  
  19. */
  20. // Quake is a trademark of Id Software, Inc., (c) 1996 Id Software, Inc. All
  21. // rights reserved.
  22.  
  23. #include <dpmi.h>
  24. #include "quakedef.h"
  25. #include "dosisms.h"
  26.  
  27. extern    cvar_t    bgmvolume;
  28.  
  29. #define ADDRESS_MODE_HSG        0
  30. #define ADDRESS_MODE_RED_BOOK    1
  31.  
  32. #define STATUS_ERROR_BIT    0x8000
  33. #define STATUS_BUSY_BIT        0x0200
  34. #define STATUS_DONE_BIT        0x0100
  35. #define STATUS_ERROR_MASK    0x00ff
  36.  
  37. #define ERROR_WRITE_PROTECT        0
  38. #define ERROR_UNKNOWN_UNIT        1
  39. #define ERROR_DRIVE_NOT_READY    2
  40. #define ERROR_UNKNOWN_COMMAND    3
  41. #define ERROR_CRC_ERROR            4
  42. #define ERROR_BAD_REQUEST_LEN    5
  43. #define ERROR_SEEK_ERROR        6
  44. #define ERROR_UNKNOWN_MEDIA        7
  45. #define ERROR_SECTOR_NOT_FOUND    8
  46. #define ERROR_OUT_OF_PAPER        9
  47. #define ERROR_WRITE_FAULT        10
  48. #define ERROR_READ_FAULT        11
  49. #define ERROR_GENERAL_FAILURE    12
  50. #define ERROR_RESERVED_13        13
  51. #define ERROR_RESERVED_14        14
  52. #define ERROR_BAD_DISK_CHANGE    15
  53.  
  54. #define COMMAND_READ            3
  55. #define COMMAND_WRITE            12
  56. #define COMMAND_PLAY_AUDIO        132
  57. #define COMMAND_STOP_AUDIO        133
  58. #define COMMAND_RESUME_AUDIO    136
  59.  
  60. #define READ_REQUEST_AUDIO_CHANNEL_INFO        4
  61. #define READ_REQUEST_DEVICE_STATUS            6
  62. #define READ_REQUEST_MEDIA_CHANGE            9
  63. #define READ_REQUEST_AUDIO_DISK_INFO        10
  64. #define READ_REQUEST_AUDIO_TRACK_INFO        11
  65. #define READ_REQUEST_AUDIO_STATUS            15
  66.  
  67. #define WRITE_REQUEST_EJECT                    0
  68. #define WRITE_REQUEST_RESET                    2
  69. #define WRITE_REQUEST_AUDIO_CHANNEL_INFO    3
  70.  
  71. #define STATUS_DOOR_OPEN                    0x00000001
  72. #define STATUS_DOOR_UNLOCKED                0x00000002
  73. #define STATUS_RAW_SUPPORT                    0x00000004
  74. #define STATUS_READ_WRITE                    0x00000008
  75. #define STATUS_AUDIO_SUPPORT                0x00000010
  76. #define STATUS_INTERLEAVE_SUPPORT            0x00000020
  77. #define STATUS_BIT_6_RESERVED                0x00000040
  78. #define STATUS_PREFETCH_SUPPORT                0x00000080
  79. #define STATUS_AUDIO_MANIPLUATION_SUPPORT    0x00000100
  80. #define STATUS_RED_BOOK_ADDRESS_SUPPORT        0x00000200
  81.  
  82. #define MEDIA_NOT_CHANGED        1
  83. #define MEDIA_STATUS_UNKNOWN    0
  84. #define MEDIA_CHANGED            -1
  85.  
  86. #define AUDIO_CONTROL_MASK                0xd0
  87. #define AUDIO_CONTROL_DATA_TRACK        0x40
  88. #define AUDIO_CONTROL_AUDIO_2_TRACK        0x00
  89. #define AUDIO_CONTROL_AUDIO_2P_TRACK    0x10
  90. #define AUDIO_CONTROL_AUDIO_4_TRACK        0x80
  91. #define AUDIO_CONTROL_AUDIO_4P_TRACK    0x90
  92.  
  93. #define AUDIO_STATUS_PAUSED                0x0001
  94.  
  95. #pragma pack(1)
  96.  
  97. struct playAudioRequest
  98. {
  99.     char    addressingMode;
  100.     int        startLocation;
  101.     int        sectors;
  102. };
  103.  
  104. struct readRequest
  105. {
  106.     char    mediaDescriptor;
  107.     short    bufferOffset;
  108.     short    bufferSegment;
  109.     short    length;
  110.     short    startSector;
  111.     int        volumeID;
  112. };
  113.  
  114. struct writeRequest
  115. {
  116.     char    mediaDescriptor;
  117.     short    bufferOffset;
  118.     short    bufferSegment;
  119.     short    length;
  120.     short    startSector;
  121.     int        volumeID;
  122. };
  123.  
  124. struct cd_request
  125. {
  126.     char    headerLength;
  127.     char    unit;
  128.     char    command;
  129.     short    status;
  130.     char    reserved[8];
  131.     union
  132.     {
  133.         struct    playAudioRequest    playAudio;
  134.         struct    readRequest            read;
  135.         struct    writeRequest        write;
  136.     } x;
  137. };
  138.  
  139.  
  140. struct audioChannelInfo_s
  141. {
  142.     char    code;
  143.     char    channel0input;
  144.     char    channel0volume;
  145.     char    channel1input;
  146.     char    channel1volume;
  147.     char    channel2input;
  148.     char    channel2volume;
  149.     char    channel3input;
  150.     char    channel3volume;
  151. };
  152.  
  153. struct deviceStatus_s
  154. {
  155.     char    code;
  156.     int        status;
  157. };
  158.  
  159. struct mediaChange_s
  160. {
  161.     char    code;
  162.     char    status;
  163. };
  164.  
  165. struct audioDiskInfo_s
  166. {
  167.     char    code;
  168.     char    lowTrack;
  169.     char    highTrack;
  170.     int        leadOutStart;
  171. };
  172.  
  173. struct audioTrackInfo_s
  174. {
  175.     char    code;
  176.     char    track;
  177.     int        start;
  178.     char    control;
  179. };
  180.  
  181. struct audioStatus_s
  182. {
  183.     char    code;
  184.     short    status;
  185.     int        PRstartLocation;
  186.     int        PRendLocation;
  187. };
  188.  
  189. struct reset_s
  190. {
  191.     char    code;
  192. };
  193.  
  194. union readInfo_u
  195. {
  196.     struct audioChannelInfo_s    audioChannelInfo;
  197.     struct deviceStatus_s        deviceStatus;
  198.     struct mediaChange_s        mediaChange;
  199.     struct audioDiskInfo_s        audioDiskInfo;
  200.     struct audioTrackInfo_s        audioTrackInfo;
  201.     struct audioStatus_s        audioStatus;
  202.     struct reset_s                reset;
  203. };
  204.  
  205. #pragma pack()
  206.  
  207. #define MAXIMUM_TRACKS            100
  208.  
  209. typedef struct
  210. {
  211.     int            start;
  212.     int            length;
  213.     qboolean    isData;
  214. } track_info;
  215.  
  216. typedef struct
  217. {
  218.     qboolean    valid;
  219.     int            leadOutAddress;
  220.     track_info    track[MAXIMUM_TRACKS];
  221.     byte        lowTrack;
  222.     byte        highTrack;
  223. } cd_info;
  224.  
  225. static struct cd_request    *cdRequest;
  226. static union readInfo_u        *readInfo;
  227. static cd_info                cd;
  228.  
  229. static qboolean    playing = false;
  230. static qboolean    wasPlaying = false;
  231. static qboolean    mediaCheck = false;
  232. static qboolean    initialized = false;
  233. static qboolean    enabled = true;
  234. static qboolean playLooping = false;
  235. static short    cdRequestSegment;
  236. static short    cdRequestOffset;
  237. static short    readInfoSegment;
  238. static short    readInfoOffset;
  239. static byte     remap[256];
  240. static byte        cdrom;
  241. static byte        playTrack;
  242. static byte        cdvolume;
  243.  
  244.  
  245. static int RedBookToSector(int rb)
  246. {
  247.     byte    minute;
  248.     byte    second;
  249.     byte    frame;
  250.  
  251.     minute = (rb >> 16) & 0xff;
  252.     second = (rb >> 8) & 0xff;
  253.     frame = rb & 0xff;
  254.     return minute * 60 * 75 + second * 75 + frame;
  255. }
  256.  
  257.  
  258. static void CDAudio_Reset(void)
  259. {
  260.     cdRequest->headerLength = 13;
  261.     cdRequest->unit = 0;
  262.     cdRequest->command = COMMAND_WRITE;
  263.     cdRequest->status = 0;
  264.  
  265.     cdRequest->x.write.mediaDescriptor = 0;
  266.     cdRequest->x.write.bufferOffset = readInfoOffset;
  267.     cdRequest->x.write.bufferSegment = readInfoSegment;
  268.     cdRequest->x.write.length = sizeof(struct reset_s);
  269.     cdRequest->x.write.startSector = 0;
  270.     cdRequest->x.write.volumeID = 0;
  271.  
  272.     readInfo->reset.code = WRITE_REQUEST_RESET;
  273.  
  274.     regs.x.ax = 0x1510;
  275.     regs.x.cx = cdrom;
  276.     regs.x.es = cdRequestSegment;
  277.     regs.x.bx = cdRequestOffset;
  278.     dos_int86 (0x2f);
  279. }
  280.  
  281.  
  282. static void CDAudio_Eject(void)
  283. {
  284.     cdRequest->headerLength = 13;
  285.     cdRequest->unit = 0;
  286.     cdRequest->command = COMMAND_WRITE;
  287.     cdRequest->status = 0;
  288.  
  289.     cdRequest->x.write.mediaDescriptor = 0;
  290.     cdRequest->x.write.bufferOffset = readInfoOffset;
  291.     cdRequest->x.write.bufferSegment = readInfoSegment;
  292.     cdRequest->x.write.length = sizeof(struct reset_s);
  293.     cdRequest->x.write.startSector = 0;
  294.     cdRequest->x.write.volumeID = 0;
  295.  
  296.     readInfo->reset.code = WRITE_REQUEST_EJECT;
  297.  
  298.     regs.x.ax = 0x1510;
  299.     regs.x.cx = cdrom;
  300.     regs.x.es = cdRequestSegment;
  301.     regs.x.bx = cdRequestOffset;
  302.     dos_int86 (0x2f);
  303. }
  304.  
  305.  
  306. static int CDAudio_GetAudioTrackInfo(byte track, int *start)
  307. {
  308.     byte    control;
  309.  
  310.     cdRequest->headerLength = 13;
  311.     cdRequest->unit = 0;
  312.     cdRequest->command = COMMAND_READ;
  313.     cdRequest->status = 0;
  314.  
  315.     cdRequest->x.read.mediaDescriptor = 0;
  316.     cdRequest->x.read.bufferOffset = readInfoOffset;
  317.     cdRequest->x.read.bufferSegment = readInfoSegment;
  318.     cdRequest->x.read.length = sizeof(struct audioTrackInfo_s);
  319.     cdRequest->x.read.startSector = 0;
  320.     cdRequest->x.read.volumeID = 0;
  321.  
  322.     readInfo->audioTrackInfo.code = READ_REQUEST_AUDIO_TRACK_INFO;
  323.     readInfo->audioTrackInfo.track = track;
  324.  
  325.     regs.x.ax = 0x1510;
  326.     regs.x.cx = cdrom;
  327.     regs.x.es = cdRequestSegment;
  328.     regs.x.bx = cdRequestOffset;
  329.     dos_int86 (0x2f);
  330.  
  331.     if (cdRequest->status & STATUS_ERROR_BIT)
  332.     {
  333.         Con_DPrintf("CDAudio_GetAudioTrackInfo %04x\n", cdRequest->status &     0xffff);
  334.         return -1;
  335.     }
  336.  
  337.     *start = readInfo->audioTrackInfo.start;
  338.     control = readInfo->audioTrackInfo.control & AUDIO_CONTROL_MASK;
  339.     return (control & AUDIO_CONTROL_DATA_TRACK);
  340. }
  341.  
  342.  
  343. static int CDAudio_GetAudioDiskInfo(void)
  344. {
  345.     int n;
  346.  
  347.     cdRequest->headerLength = 13;
  348.     cdRequest->unit = 0;
  349.     cdRequest->command = COMMAND_READ;
  350.     cdRequest->status = 0;
  351.  
  352.     cdRequest->x.read.mediaDescriptor = 0;
  353.     cdRequest->x.read.bufferOffset = readInfoOffset;
  354.     cdRequest->x.read.bufferSegment = readInfoSegment;
  355.     cdRequest->x.read.length = sizeof(struct audioDiskInfo_s);
  356.     cdRequest->x.read.startSector = 0;
  357.     cdRequest->x.read.volumeID = 0;
  358.  
  359.     readInfo->audioDiskInfo.code = READ_REQUEST_AUDIO_DISK_INFO;
  360.  
  361.     regs.x.ax = 0x1510;
  362.     regs.x.cx = cdrom;
  363.     regs.x.es = cdRequestSegment;
  364.     regs.x.bx = cdRequestOffset;
  365.     dos_int86 (0x2f);
  366.  
  367.     if (cdRequest->status & STATUS_ERROR_BIT)
  368.     {
  369.         Con_DPrintf("CDAudio_GetAudioDiskInfo %04x\n", cdRequest->status &     0xffff);
  370.         return -1;
  371.     }
  372.  
  373.     cd.valid = true;
  374.     cd.lowTrack = readInfo->audioDiskInfo.lowTrack;
  375.     cd.highTrack = readInfo->audioDiskInfo.highTrack;
  376.     cd.leadOutAddress = readInfo->audioDiskInfo.leadOutStart;
  377.  
  378.     for (n = cd.lowTrack; n <= cd.highTrack; n++)
  379.     {
  380.         cd.track[n].isData = CDAudio_GetAudioTrackInfo (n, &cd.track[n].start);
  381.         if (n > cd.lowTrack)
  382.         {
  383.             cd.track[n-1].length = RedBookToSector(cd.track[n].start) - RedBookToSector(cd.track[n-1].start);
  384.             if (n == cd.highTrack)
  385.                 cd.track[n].length = RedBookToSector(cd.leadOutAddress) - RedBookToSector(cd.track[n].start);
  386.         }
  387.     }
  388.  
  389.     return 0;
  390. }
  391.  
  392.  
  393. static int CDAudio_GetAudioStatus(void)
  394. {
  395.     cdRequest->headerLength = 13;
  396.     cdRequest->unit = 0;
  397.     cdRequest->command = COMMAND_READ;
  398.     cdRequest->status = 0;
  399.  
  400.     cdRequest->x.read.mediaDescriptor = 0;
  401.     cdRequest->x.read.bufferOffset = readInfoOffset;
  402.     cdRequest->x.read.bufferSegment = readInfoSegment;
  403.     cdRequest->x.read.length = sizeof(struct audioStatus_s);
  404.     cdRequest->x.read.startSector = 0;
  405.     cdRequest->x.read.volumeID = 0;
  406.  
  407.     readInfo->audioDiskInfo.code = READ_REQUEST_AUDIO_STATUS;
  408.  
  409.     regs.x.ax = 0x1510;
  410.     regs.x.cx = cdrom;
  411.     regs.x.es = cdRequestSegment;
  412.     regs.x.bx = cdRequestOffset;
  413.     dos_int86 (0x2f);
  414.  
  415.     if (cdRequest->status & STATUS_ERROR_BIT)
  416.         return -1;
  417.     return 0;
  418. }
  419.  
  420.  
  421. static int CDAudio_MediaChange(void)
  422. {
  423.     cdRequest->headerLength = 13;
  424.     cdRequest->unit = 0;
  425.     cdRequest->command = COMMAND_READ;
  426.     cdRequest->status = 0;
  427.  
  428.     cdRequest->x.read.mediaDescriptor = 0;
  429.     cdRequest->x.read.bufferOffset = readInfoOffset;
  430.     cdRequest->x.read.bufferSegment = readInfoSegment;
  431.     cdRequest->x.read.length = sizeof(struct mediaChange_s);
  432.     cdRequest->x.read.startSector = 0;
  433.     cdRequest->x.read.volumeID = 0;
  434.  
  435.     readInfo->mediaChange.code = READ_REQUEST_MEDIA_CHANGE;
  436.  
  437.     regs.x.ax = 0x1510;
  438.     regs.x.cx = cdrom;
  439.     regs.x.es = cdRequestSegment;
  440.     regs.x.bx = cdRequestOffset;
  441.     dos_int86 (0x2f);
  442.  
  443.     return readInfo->mediaChange.status;
  444. }
  445.  
  446.  
  447. // we set the volume to 0 first and then to the desired volume
  448. // some cd-rom drivers seem to need it done this way
  449. void CDAudio_SetVolume (byte volume)
  450. {
  451.     if (!initialized || !enabled)
  452.         return;
  453.  
  454.     cdRequest->headerLength = 13;
  455.     cdRequest->unit = 0;
  456.     cdRequest->command = COMMAND_WRITE;
  457.     cdRequest->status = 0;
  458.  
  459.     cdRequest->x.read.mediaDescriptor = 0;
  460.     cdRequest->x.read.bufferOffset = readInfoOffset;
  461.     cdRequest->x.read.bufferSegment = readInfoSegment;
  462.     cdRequest->x.read.length = sizeof(struct audioChannelInfo_s);
  463.     cdRequest->x.read.startSector = 0;
  464.     cdRequest->x.read.volumeID = 0;
  465.  
  466.     readInfo->audioChannelInfo.code = WRITE_REQUEST_AUDIO_CHANNEL_INFO;
  467.     readInfo->audioChannelInfo.channel0input = 0;
  468.     readInfo->audioChannelInfo.channel0volume = 0;
  469.     readInfo->audioChannelInfo.channel1input = 1;
  470.     readInfo->audioChannelInfo.channel1volume = 0;
  471.     readInfo->audioChannelInfo.channel2input = 2;
  472.     readInfo->audioChannelInfo.channel2volume = 0;
  473.     readInfo->audioChannelInfo.channel3input = 3;
  474.     readInfo->audioChannelInfo.channel3volume = 0;
  475.  
  476.     regs.x.ax = 0x1510;
  477.     regs.x.cx = cdrom;
  478.     regs.x.es = cdRequestSegment;
  479.     regs.x.bx = cdRequestOffset;
  480.     dos_int86 (0x2f);
  481.  
  482.     readInfo->audioChannelInfo.channel0volume = volume;
  483.     readInfo->audioChannelInfo.channel1volume = volume;
  484.  
  485.     regs.x.ax = 0x1510;
  486.     regs.x.cx = cdrom;
  487.     regs.x.es = cdRequestSegment;
  488.     regs.x.bx = cdRequestOffset;
  489.     dos_int86 (0x2f);
  490.  
  491.     cdvolume = volume;
  492. }
  493.  
  494.  
  495. void CDAudio_Play(byte track, qboolean looping)
  496. {
  497.     int        volume;
  498.  
  499.     if (!initialized || !enabled)
  500.         return;
  501.     
  502.     if (!cd.valid)
  503.         return;
  504.  
  505.     track = remap[track];
  506.  
  507.     if (playing)
  508.     {
  509.         if (playTrack == track)
  510.             return;
  511.         CDAudio_Stop();
  512.     }
  513.  
  514.     playLooping = looping;
  515.  
  516.     if (track < cd.lowTrack || track > cd.highTrack)
  517.     {
  518.         Con_DPrintf("CDAudio_Play: Bad track number %u.\n", track);
  519.         return;
  520.     }
  521.  
  522.     playTrack = track;
  523.  
  524.     if (cd.track[track].isData)
  525.     {
  526.         Con_DPrintf("CDAudio_Play: Can not play data.\n");
  527.         return;
  528.     }
  529.  
  530.     volume = (int)(bgmvolume.value * 255.0);
  531.     if (volume < 0)
  532.     {
  533.         Cvar_SetValue ("bgmvolume", 0.0);
  534.         volume = 0;
  535.     }
  536.     else if (volume > 255)
  537.     {
  538.         Cvar_SetValue ("bgmvolume", 1.0);
  539.         volume = 255;
  540.     }
  541.     CDAudio_SetVolume (volume);
  542.  
  543.     cdRequest->headerLength = 13;
  544.     cdRequest->unit = 0;
  545.     cdRequest->command = COMMAND_PLAY_AUDIO;
  546.     cdRequest->status = 0;
  547.  
  548.     cdRequest->x.playAudio.addressingMode = ADDRESS_MODE_RED_BOOK;
  549.     cdRequest->x.playAudio.startLocation = cd.track[track].start;
  550.     cdRequest->x.playAudio.sectors = cd.track[track].length;
  551.  
  552.     regs.x.ax = 0x1510;
  553.     regs.x.cx = cdrom;
  554.     regs.x.es = cdRequestSegment;
  555.     regs.x.bx = cdRequestOffset;
  556.     dos_int86 (0x2f);
  557.  
  558.     if (cdRequest->status & STATUS_ERROR_BIT)
  559.     {
  560.         Con_DPrintf("CDAudio_Play: track %u failed\n", track);
  561.         cd.valid = false;
  562.         playing = false;
  563.         return;
  564.     }
  565.  
  566.     playing = true;
  567. }
  568.  
  569.  
  570. void CDAudio_Stop(void)
  571. {
  572.     if (!initialized || !enabled)
  573.         return;
  574.     
  575.     cdRequest->headerLength = 13;
  576.     cdRequest->unit = 0;
  577.     cdRequest->command = COMMAND_STOP_AUDIO;
  578.     cdRequest->status = 0;
  579.  
  580.     regs.x.ax = 0x1510;
  581.     regs.x.cx = cdrom;
  582.     regs.x.es = cdRequestSegment;
  583.     regs.x.bx = cdRequestOffset;
  584.     dos_int86 (0x2f);
  585.  
  586.     wasPlaying = playing;
  587.     playing = false;
  588. }
  589.  
  590.  
  591. void CDAudio_Pause(void)
  592. {
  593.     CDAudio_Stop();
  594. }
  595.  
  596.  
  597. void CDAudio_Resume(void)
  598. {
  599.     if (!initialized || !enabled)
  600.         return;
  601.     
  602.     if (!cd.valid)
  603.         return;
  604.  
  605.     if (!wasPlaying)
  606.         return;
  607.     
  608.     cdRequest->headerLength = 13;
  609.     cdRequest->unit = 0;
  610.     cdRequest->command = COMMAND_RESUME_AUDIO;
  611.     cdRequest->status = 0;
  612.  
  613.     regs.x.ax = 0x1510;
  614.     regs.x.cx = cdrom;
  615.     regs.x.es = cdRequestSegment;
  616.     regs.x.bx = cdRequestOffset;
  617.     dos_int86 (0x2f);
  618.  
  619.     playing = true;
  620. }
  621.  
  622.  
  623. static void CD_f (void)
  624. {
  625.     char    *command;
  626.     int        ret;
  627.     int        n;
  628.     int        startAddress;
  629.  
  630.     if (Cmd_Argc() < 2)
  631.         return;
  632.  
  633.     command = Cmd_Argv (1);
  634.  
  635.     if (Q_strcasecmp(command, "on") == 0)
  636.     {
  637.         enabled = true;
  638.         return;
  639.     }
  640.  
  641.     if (Q_strcasecmp(command, "off") == 0)
  642.     {
  643.         if (playing)
  644.             CDAudio_Stop();
  645.         enabled = false;
  646.         return;
  647.     }
  648.  
  649.     if (Q_strcasecmp(command, "reset") == 0)
  650.     {
  651.         enabled = true;
  652.         if (playing)
  653.             CDAudio_Stop();
  654.         for (n = 0; n < 256; n++)
  655.             remap[n] = n;
  656.         CDAudio_Reset();
  657.         CDAudio_GetAudioDiskInfo();
  658.         return;
  659.     }
  660.  
  661.     if (Q_strcasecmp(command, "remap") == 0)
  662.     {
  663.         ret = Cmd_Argc() - 2;
  664.         if (ret <= 0)
  665.         {
  666.             for (n = 1; n < 256; n++)
  667.                 if (remap[n] != n)
  668.                     Con_Printf("  %u -> %u\n", n, remap[n]);
  669.             return;
  670.         }
  671.         for (n = 1; n <= ret; n++)
  672.             remap[n] = Q_atoi(Cmd_Argv (n+1));
  673.         return;
  674.     }
  675.  
  676.     if (!cd.valid)
  677.     {
  678.         Con_Printf("No CD in player.\n");
  679.         return;
  680.     }
  681.  
  682.     if (Q_strcasecmp(command, "play") == 0)
  683.     {
  684.         CDAudio_Play(Q_atoi(Cmd_Argv (2)), false);
  685.         return;
  686.     }
  687.  
  688.     if (Q_strcasecmp(command, "loop") == 0)
  689.     {
  690.         CDAudio_Play(Q_atoi(Cmd_Argv (2)), true);
  691.         return;
  692.     }
  693.  
  694.     if (Q_strcasecmp(command, "stop") == 0)
  695.     {
  696.         CDAudio_Stop();
  697.         return;
  698.     }
  699.  
  700.     if (Q_strcasecmp(command, "pause") == 0)
  701.     {
  702.         CDAudio_Pause();
  703.         return;
  704.     }
  705.  
  706.     if (Q_strcasecmp(command, "resume") == 0)
  707.     {
  708.         CDAudio_Resume();
  709.         return;
  710.     }
  711.  
  712.     if (Q_strcasecmp(command, "eject") == 0)
  713.     {
  714.         if (playing)
  715.             CDAudio_Stop();
  716.         CDAudio_Eject();
  717.         cd.valid = false;
  718.         return;
  719.     }
  720.  
  721.     if (Q_strcasecmp(command, "info") == 0)
  722.     {
  723.         Con_Printf("%u tracks\n", cd.highTrack - cd.lowTrack + 1);
  724.         for (n = cd.lowTrack; n <= cd.highTrack; n++)
  725.         {
  726.             ret = CDAudio_GetAudioTrackInfo (n, &startAddress);
  727.             Con_Printf("Track %2u: %s at %2u:%02u\n", n, ret ? "data " : "music", (startAddress >> 16) & 0xff, (startAddress >> 8) & 0xff);
  728.         }
  729.         if (playing)
  730.             Con_Printf("Currently %s track %u\n", playLooping ? "looping" : "playing", playTrack);
  731.         Con_Printf("Volume is %u\n", cdvolume);
  732.         CDAudio_MediaChange();
  733.         Con_Printf("Status %04x\n", cdRequest->status & 0xffff);
  734.         return;
  735.     }
  736. }
  737.  
  738.  
  739. void CDAudio_Update(void)
  740. {
  741.     int        ret;
  742.     int        newVolume;
  743.     static    double lastUpdate;
  744.  
  745.     if (!initialized || !enabled)
  746.         return;
  747.  
  748.     if ((realtime - lastUpdate) < 0.25)
  749.         return;
  750.     lastUpdate = realtime;
  751.  
  752.     if (mediaCheck)
  753.     {
  754.         static    double lastCheck;
  755.  
  756.         if ((realtime - lastCheck) < 5.0)
  757.             return;
  758.         lastCheck = realtime;
  759.  
  760.         ret = CDAudio_MediaChange();
  761.         if (ret == MEDIA_CHANGED)
  762.         {
  763.             Con_DPrintf("CDAudio: media changed\n");
  764.             playing = false;
  765.             wasPlaying = false;
  766.             cd.valid = false;
  767.             CDAudio_GetAudioDiskInfo();
  768.             return;
  769.         }
  770.     }
  771.  
  772.     newVolume = (int)(bgmvolume.value * 255.0);
  773.     if (newVolume != cdvolume)
  774.     {
  775.         if (newVolume < 0)
  776.         {
  777.             Cvar_SetValue ("bgmvolume", 0.0);
  778.             newVolume = 0;
  779.         }
  780.         else if (newVolume > 255)
  781.         {
  782.             Cvar_SetValue ("bgmvolume", 1.0);
  783.             newVolume = 255;
  784.         }
  785.         CDAudio_SetVolume (newVolume);
  786.     }
  787.  
  788.     if (playing)
  789.     {
  790.         CDAudio_GetAudioStatus();
  791.         if ((cdRequest->status & STATUS_BUSY_BIT) == 0)
  792.         {
  793.             playing = false;
  794.             if (playLooping)
  795.                 CDAudio_Play(playTrack, true);
  796.         }
  797.     }
  798. }
  799.  
  800.  
  801. int CDAudio_Init(void)
  802. {
  803.     char    *memory;
  804.     int        n;
  805.  
  806.     if (cls.state == ca_dedicated)
  807.         return -1;
  808.  
  809.     if (COM_CheckParm("-nocdaudio"))
  810.         return -1;
  811.  
  812.     if (COM_CheckParm("-cdmediacheck"))
  813.         mediaCheck = true;
  814.  
  815.     regs.x.ax = 0x1500;
  816.     regs.x.bx = 0;
  817.     dos_int86 (0x2f);
  818.     if (regs.x.bx == 0)
  819.     {
  820.         Con_NotifyBox (
  821.             "MSCDEX not loaded, music is\n"
  822.             "disabled.  Use \"-nocdaudio\" if you\n"
  823.             "wish to avoid this message in the\n"
  824.             "future.  See README.TXT for help.\n"
  825.             );            
  826.         return -1;
  827.     }
  828.     if (regs.x.bx > 1)
  829.         Con_DPrintf("CDAudio_Init: First CD-ROM drive will be used\n");
  830.     cdrom = regs.x.cx;
  831.  
  832.     regs.x.ax = 0x150c;
  833.     regs.x.bx = 0;
  834.     dos_int86 (0x2f);
  835.     if (regs.x.bx == 0)
  836.     {
  837.         Con_NotifyBox (
  838.             "MSCDEX version 2.00 or later\n"
  839.             "required for music. See README.TXT\n"
  840.             "for help.\n"
  841.             );            
  842.         Con_DPrintf("CDAudio_Init: MSCDEX version 2.00 or later required.\n");
  843.         return -1;
  844.     }
  845.  
  846.     memory = dos_getmemory(sizeof(struct cd_request
  847. ) + sizeof(union readInfo_u));
  848.     if (memory == NULL)
  849.     {
  850.         Con_DPrintf("CDAudio_Init: Unable to allocate low memory.\n");
  851.         return -1;
  852.     }
  853.  
  854.     cdRequest = (struct cd_request *)memory;
  855.     cdRequestSegment = ptr2real(cdRequest) >> 4;
  856.     cdRequestOffset = ptr2real(cdRequest) & 0xf;
  857.  
  858.     readInfo = (union readInfo_u *)(memory + sizeof(struct cd_request));
  859.     readInfoSegment = ptr2real(readInfo) >> 4;
  860.     readInfoOffset = ptr2real(readInfo) & 0xf;
  861.  
  862.     for (n = 0; n < 256; n++)
  863.         remap[n] = n;
  864.     initialized = true;
  865.  
  866.     CDAudio_SetVolume (255);
  867.     if (CDAudio_GetAudioDiskInfo())
  868.     {
  869.         Con_Printf("CDAudio_Init: No CD in player.\n");
  870.         enabled = false;
  871.     }
  872.  
  873.     Cmd_AddCommand ("cd", CD_f);
  874.  
  875.     Con_Printf("CD Audio Initialized\n");
  876.  
  877.     return 0;
  878. }
  879.  
  880.  
  881. void CDAudio_Shutdown(void)
  882. {
  883.     if (!initialized)
  884.         return;
  885.     CDAudio_Stop();
  886. }
  887.